
import { HvigorNode, HvigorPlugin, Json5Reader } from '@ohos/hvigor';
import * as fs from 'node:fs';
import path from 'path';
import Handlebars from "handlebars";
import ts, { CallExpression, Decorator, ExpressionStatement, Identifier, MethodDeclaration, ParameterDeclaration, PropertyAssignment, StringLiteral, StringLiteralType } from 'typescript';


const ROUTER_PLUGIN = "RouterPlugin"

const ROUTER = "Router"

const ROUTER_GENERATE = "generate"


export class RouterConfig {
    // 需要扫描的文件夹名称 及其子目录
    scanPackages: string[] = [];
    // 不需要扫描的路径或者文件
    ignorePath: string[] = [];
}

export function RouterPlugin(config?: RouterConfig): HvigorPlugin {

    return {
        pluginId: ROUTER_PLUGIN,
        apply(node: HvigorNode) {
            const modulePath = node.getNodePath()
            const moduleDirName = path.basename(node.getNodePath())
            console.log(ROUTER_PLUGIN + "：================================================================================");
            console.log(ROUTER_PLUGIN + "：模块目录-" + modulePath);
            let fileList: string[] = []
            console.log(ROUTER_PLUGIN + "：模块目录文件-" + moduleDirName);
            const filePath = modulePath + "/src/main/ets"

            let recursion = true
            if (config?.scanPackages && config?.scanPackages.length > 0) {
                recursion = config.scanPackages.includes(filePath)
            }
            fileList.push(...getFileByFilter(filePath, config?.scanPackages, config?.ignorePath, recursion))


            let analyzer: Analyzer
            let routerInfoList: RouterInfo[] = []
            let routerBuilderInfoList: RouterBuilderInfo[] = []
            fileList.forEach((etsPath) => {
                //如果是自动生成的文件则忽略
                let fileName = path.basename(etsPath)

                analyzer = new Analyzer(ROUTER, etsPath, modulePath)

                //解析组件内容，获取自定义注解参数
                analyzer.start()
                if (analyzer.routerInfo && analyzer.routerBuilderInfo) {
                    routerBuilderInfoList.push(analyzer.routerBuilderInfo)
                    routerInfoList.push(analyzer.routerInfo)
                }
            })
            const moduleJson5Path = modulePath + "/src/main/module.json5"
            const moduleJson5 = Json5Reader.getJson5Obj(moduleJson5Path, "utf8")
            const module = Json5Reader.getJson5ObjProp(moduleJson5, "module")
            const routerMapPath = Json5Reader.getJson5ObjProp(module, "routerMap")
            const routerMapDir = modulePath + "/src/main/resources/base/profile/"
            if (!routerMapPath) {
                moduleJson5.module.routerMap = "$profile:router_map"
                fs.writeFileSync(moduleJson5Path, JSON.stringify(moduleJson5, null, 2), { encoding: "utf8" })
                let routerMap = {
                    routerMap: routerInfoList
                }
                fs.writeFileSync(routerMapDir + "router_map.json", JSON.stringify(routerMap, null, 2), { encoding: "utf8" })
                console.log(ROUTER_PLUGIN + "：创建系统路由表router_map.json");
            } else {
                const routerMapRealPath = (routerMapPath as string).replace("$profile:", routerMapDir) + ".json"
                try {
                    let routerMapObj = JSON.parse(fs.readFileSync(routerMapRealPath, { encoding: "utf8" }))
                    let filterList: RouterInfo[] = []
                    routerInfoList.forEach(info => {
                        let originInfo = routerMapObj.routerMap.find((i: any) => i.name == info.name)
                        if (originInfo) {
                            originInfo.buildFunction = info.buildFunction
                            originInfo.pageSourceFile = info.pageSourceFile
                            if (info.data) {
                                originInfo.data = info.data
                            }
                        } else {
                            filterList.push(info)
                        }
                    })
                    routerMapObj.routerMap.push(...filterList)
                    fs.writeFileSync(routerMapRealPath, JSON.stringify(routerMapObj, null, 2), { encoding: "utf8" })
                    log("写入系统路由表")
                } catch (e) {
                    let routerMap = {
                        routerMap: routerInfoList
                    }
                    fs.writeFileSync(routerMapRealPath, JSON.stringify(routerMap, null, 2), { encoding: "utf8" })
                    log("写入系统路由表")
                }
            }

            generateRouterBuilderFile(routerBuilderInfoList, modulePath)

            console.log(ROUTER_PLUGIN + "：================================================================================");
        }
    }

}

function log(msg: string) {
    console.log(ROUTER_PLUGIN + "：" + msg)
}

function generateRouterBuilderFile(routerBuilderInfo: RouterBuilderInfo[], modulePath: string) {
    const builderPath = __dirname + "/hrouter-builder.tpl";
    const tpl = fs.readFileSync(builderPath, { encoding: "utf8" });
    const template = Handlebars.compile(tpl);
    routerBuilderInfo.forEach(info => {
        const output = template({
            componentName: info.componentName,
            pageSourceFile: info.pageSourceFile,
            componentBuilder: info.componentBuilder
        });

        const routerBuilderDir = modulePath + "/src/main/ets" + `/${ROUTER_GENERATE}`;
        if (!fs.existsSync(routerBuilderDir)) {
            fs.mkdirSync(routerBuilderDir, { recursive: true });
        }
        fs.writeFileSync(`${routerBuilderDir}/${info.componentBuilder}.ets`, output, { encoding: "utf8" });
        log("创建路由builder-" + info.componentBuilder + ".ets")
    })
}

function getFileByFilter(filePath: string, scanPackages: string[] | undefined, ignorePath: string[] | undefined, recursion: boolean): string[] {
    if (!filePath) {
        return []
    }
    // 忽略扫描的文件夹
    if (ignorePath && ignorePath.length > 0) {
        if (ignorePath.includes(filePath)) {
            return []
        }
        // 模糊匹配，以什么命名的文件夹
        for (const value in ignorePath) {
            if (filePath.endsWith(value)) {
                return []
            }
        }
    }
    // 过滤自动生成
    if (filePath.endsWith("src/main/ets/" + ROUTER_GENERATE)) {
        return []
    }

    const stats = fs.lstatSync(filePath)
    const fileArray: string[] = []
    if (stats.isFile()) {
        if (recursion) {
            if (filePath.endsWith(".ets") || filePath.endsWith(".ETS")) {
                fileArray.push(filePath)
            }
        }
        return fileArray
    } else if (stats.isDirectory()) {
        let dirName = path.basename(filePath)
        fs.readdirSync(filePath).forEach(file => {
            const fullPath = path.join(filePath, file);
            let isRecursion = recursion
            if (!recursion && scanPackages && scanPackages.length > 0) {
                recursion = scanPackages.includes(dirName)
            }
            //scanPackageNameChild=true 如果某个文件夹名字需要扫描，则该文件下下面所有的子文件都需要扫描
            fileArray.push(...getFileByFilter(fullPath, scanPackages, ignorePath, isRecursion))
        });

        return fileArray
    } else {
        return []
    }
}

/** 
 * "name": "PageOne",
 *  "pageSourceFile": "src/main/ets/pages/PageOne.ets",
 * "buildFunction": "PageOneBuilder",
 * "data": {
 *   "description" : "this is PageOne"
 * }
 */
class RouterInfo {
    name: string | undefined
    pageSourceFile: string | undefined
    buildFunction: string | undefined
    data: Data | undefined
}

class RouterBuilderInfo {
    componentName: string | undefined
    pageSourceFile: string | undefined
    componentBuilder: string | undefined
}


class Data {
    description: string
    constructor(description: string) {
        this.description = description
    }
}

class Analyzer {

    //路由注解名字
    private annotation: string
    //扫描到的ets文件路径
    private filePath: string
    private modulePath: string

    routerInfo: RouterInfo | undefined

    routerBuilderInfo: RouterBuilderInfo | undefined

    constructor(annotation: string, filePath: string, modulePath: string) {
        this.annotation = annotation;
        this.filePath = filePath;
        this.modulePath = modulePath
    }



    start() {
        const sourceCode = fs.readFileSync(this.filePath, 'utf-8');
        const sourceFile = ts.createSourceFile(this.filePath, sourceCode, ts.ScriptTarget.ES2021, false);
        // 遍历节点信息
        ts.forEachChild(sourceFile, (node: ts.Node) => {
            // 解析节点
            this.resolveNode(node, sourceFile);
        });
    }

    findComponetNode(pos: number, sourceFile: ts.SourceFile): ts.Node | undefined {
        let findNode: ts.Node | undefined = undefined
        ts.forEachChild(sourceFile, (node: ts.Node) => {
            // 解析节点
            if (node.pos == pos) {
                findNode = node
            }
        });
        return findNode
    }
    resolveNode(node: ts.Node, sourceFile: ts.SourceFile) {
        if (node.kind != ts.SyntaxKind.MissingDeclaration) {
            return false
        }

        //@Component+@自定义装饰器
        let child = node as ts.ParameterDeclaration
        let modifiers = child.modifiers
        //如果装饰器不为空且大于等于2
        if (modifiers && modifiers.length >= 2) {

            modifiers.forEach((node) => {
                let decorator = node as Decorator
                if (decorator && decorator.expression) {
                    let callExpression = decorator.expression as CallExpression
                    if (callExpression.kind == ts.SyntaxKind.CallExpression) {

                        //如果有注解和参数不为空
                        let arg = callExpression.arguments
                        if (callExpression.expression && arg && arg.length > 0) {
                            //如果注解名字匹配
                            if (this.annotation == (callExpression.expression as Identifier).escapedText) {
                               log("扫描到需要解析的HRouter-" + this.filePath);
                                let properties: ts.NodeArray<StringLiteral> = arg as ts.NodeArray<StringLiteral>
                                let name = properties[0].text
                                if (!name.startsWith("/")) {
                                    console.error(ROUTER_PLUGIN + "：路由地址需要以/开头-"+name)
                                } else {
                                    this.routerInfo = new RouterInfo()
                                    this.routerBuilderInfo = new RouterBuilderInfo()
                                    let componentNode = this.findComponetNode(child.end + 7, sourceFile) as ExpressionStatement
                                    let componentIdentifier = componentNode.expression as Identifier
                                    let componentName = componentIdentifier.escapedText!
                                    this.routerBuilderInfo.componentName = componentName



                                    this.routerInfo.name = name
                                    if (properties.length > 1) {
                                        let description = properties[1].text
                                        this.routerInfo.data = new Data(description)
                                    }
                                    let componentBuilder = name.substring(1, name.length).replace("/", "_")
                                    this.routerInfo.pageSourceFile = 'src/main/ets/' + ROUTER_GENERATE + "/" + componentBuilder + ".ets"
                                    this.routerInfo.buildFunction = componentBuilder + "Builder"
                                    this.routerBuilderInfo.componentBuilder = componentBuilder
                                    this.routerBuilderInfo.pageSourceFile = this.filePath.replace(this.modulePath + "/src/main/", "").replace(".ets", "")
                                }

                            }
                        }
                    }
                }
            })
        }
    }

}